package com.xaicode.learn.nio;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import static com.xaicode.learn.util.timer.TimerUtil.SEC_1;

/**
 * @author Locker xaicode@sina.com
 * @version 1.0
 * @date 2021/6/13 0013 18:39
 */
@Slf4j
public class NIOServer {

    public static void main(String[] args) throws IOException {

        // create server socket channel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // get selector
        Selector selector = Selector.open();

        // server socket channel bind port and listen
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        // to NIO
        serverSocketChannel.configureBlocking(false);

        // register server socket channel to selector and listen ACCEPT event
        // one of OP_READ/OP_WRITE/OP_CONNECT/OP_ACCEPT
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {

            // there is no event in 1s, continue
            if (selector.select(SEC_1) == 0) {
                log.info("there is no event in 1s, continue listen");
                continue;
            }

            // if select(mill) > 0, there is some event happened on selected type (for now is OP_ACCEPT)
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                // get selection key
                SelectionKey selectionKey = iterator.next();

                // Tests whether this key's channel is ready to accept a new socket connection.
                if (selectionKey.isAcceptable()) {
                    // create a new socket channel for selection key
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    // to NIO
                    socketChannel.configureBlocking(false);
                    log.info("new client [{}] connected", socketChannel.hashCode());
                    // register socket channel to selector
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }

                // Tests whether this key's channel is ready for reading.
                if (selectionKey.isReadable()) {
                    // selectionKey to channel
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    // get selection-key buffer
                    ByteBuffer buffer = (ByteBuffer) selectionKey.attachment();
                    // buffer to channel
                    channel.read(buffer);

                    log.info("get client content [{}]", new String(buffer.array()));
                }

                // remove this selection key for preventing repetitive operation
                iterator.remove();
            }

        }

    }

}
